package vendor_account

import (
	"context"
	model "micro-oauth/datamodels"
	"micro-oauth/datamodels/autherror"
	"micro-oauth/datamodels/enum"
	"micro-oauth/datamodels/params"
	"micro-oauth/repo"
	"time"
)

type VendorAccountServiceImpl struct {
	repoService repo.ServiceInterface
}

func NewVendorAccountService(repoService repo.ServiceInterface) VendorAccountService {
	return &VendorAccountServiceImpl{
		repoService: repoService,
	}
}

func (s *VendorAccountServiceImpl) ListVendorUser(ctx context.Context, req *params.VendorUserListRequest) ([]*model.User, uint64, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	scopeId := ctx.Value("scope_id").(uint64)
	req.PartnerId = pId
	req.ScopeId = scopeId
	users, total, err := s.repoService.ListVendorUser(req)
	if err != nil {
		return nil, 0, autherror.DBError(err.Error())
	}
	for _, user := range users {
		if user.Type == enum.UserType_VendorUser {
			user.Name = ""
		} else if user.Type == enum.UserType_VendorManager {
			user.Email = ""
		}
	}
	return users, total, nil
}

func (s *VendorAccountServiceImpl) TriggerVendorUserStatus(ctx context.Context, req *params.TriggerVendorUserStatusRequest) autherror.HexError {
	pId := ctx.Value("partner_id").(uint64)
	uId := ctx.Value("user_id").(uint64)
	scopeId := ctx.Value("scope_id").(uint64)
	err := req.Validate()
	if err != nil {
		return autherror.InvalidData(err.Error())
	}
	user := s.repoService.GetUserById(req.UserId)
	if user.PartnerId != pId {
		return autherror.PermissionDenied("wrong partner")
	}
	if user.ScopeId != scopeId {
		return autherror.PermissionDenied("wrong scope")
	}
	user.Status = req.Status
	user.UpdatedBy = uId
	user.UpdatedAt = time.Now()
	err = s.repoService.UpdateUser(user)
	if err != nil {
		return autherror.DBError(err.Error())
	}
	return nil
}

func (s *VendorAccountServiceImpl) CreateVendorUser(ctx context.Context, req *params.VendorUserCreateRequest) (*model.User, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	uId := ctx.Value("user_id").(uint64)
	scopeId := ctx.Value("scope_id").(uint64)
	err := req.Validate()
	if err != nil {
		return nil, autherror.InvalidData(err.Error())
	}
	userListReq := &params.UserListRequest{
		ListRequest: params.ListRequest{
			Limit:     1,
			PartnerId: pId,
			ScopeId:   scopeId,
		},
		ThirdPartyId: req.VendorId,
	}
	_, total, err := s.repoService.ListUser(userListReq)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	if total != 0 {
		return nil, autherror.InvalidData("vendor account already exist")
	}

	// and pre check
	listReq := &params.UserListRequest{}
	listReq.PartnerId = pId
	listReq.ScopeId = scopeId
	listReq.Mail = req.Email
	listReq.Limit = 1
	duplicateNameUsers, _, _ := s.repoService.ListUser(listReq)
	if len(duplicateNameUsers) != 0 {
		return nil,autherror.InvalidData("mail already exist")
	}
	listReq.Mail = ""
	listReq.Name = req.Email
	duplicateCodeUsers, _, _ := s.repoService.ListUser(listReq)
	if len(duplicateCodeUsers) != 0 {
		return  nil,autherror.InvalidData("name already exist")
	}
	listReq.Name = ""
	listReq.Mail = ""
	listReq.Code = req.Email
	duplicateMailUsers, _, _ := s.repoService.ListUser(listReq)
	if len(duplicateMailUsers) != 0 {
		return  nil,autherror.InvalidData("code already exist")
	}

	user := &model.User{
		PartnerId:    pId,
		Name:         req.Email,
		Code:         req.Email,
		Email:        req.Email,
		ThirdPartyId: req.VendorId,
		ScopeId:      scopeId,
		MyGormModel: model.MyGormModel{
			CreatedBy: uId,
			UpdatedBy: uId,
		},
		Type:   enum.UserType_VendorUser,
		Status: enum.UserStatus_Active,
	}
	pwd := RandomPwd()
	user.ResetPwd(pwd)
	err = s.repoService.AddUser(user)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	err = SendPwdInitEmail(user.Email, user.Email, pwd)
	if err != nil {
		s.repoService.DeleteUser(user.Id)
		return nil, autherror.InvalidData(err.Error())
	}
	return user, nil
}

func (s *VendorAccountServiceImpl) CreateVendorManagerUser(ctx context.Context, req *params.VendorManagerCreateRequest) (*model.User, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	uId := ctx.Value("user_id").(uint64)
	scopeId := ctx.Value("scope_id").(uint64)
	err := req.Validate()
	if err != nil {
		return nil, autherror.InvalidData(err.Error())
	}
	userListReq := &params.UserListRequest{
		ListRequest: params.ListRequest{
			PartnerId: pId,
			ScopeId:   scopeId,
			Limit:     1,
		},
		Name: req.Name,
	}
	_, total, err := s.repoService.ListUser(userListReq)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	if total != 0 {
		return nil, autherror.InvalidData("name already exist")
	}

	user := &model.User{
		PartnerId: pId,
		Name:      req.Name,
		Code:      RandomCode(),
		ScopeId:   scopeId,
		MyGormModel: model.MyGormModel{
			CreatedBy: uId,
			UpdatedBy: uId,
		},
		Type:   enum.UserType_VendorManager,
		Status: enum.UserStatus_Active,
	}
	user.ResetPwd(req.PWD)
	err = s.repoService.AddUser(user)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	return user, nil
}

func (s *VendorAccountServiceImpl) ResetVendorUserPwd(ctx context.Context, req *params.ResetVendorUserPwdRequest) autherror.HexError {
	pId := ctx.Value("partner_id").(uint64)
	uId := ctx.Value("user_id").(uint64)
	scopeId := ctx.Value("scope_id").(uint64)
	if err := req.Validate(); err != nil {
		return autherror.InvalidData(err.Error())
	}
	user := s.repoService.GetUserById(req.UserId)
	if user == nil {
		return autherror.NotFound("can not find user")
	}
	if user.Type != enum.UserType_VendorUser {
		return autherror.InvalidData("user not vendor type")
	}
	if user.PartnerId != pId {
		return autherror.PermissionDenied("wrong partner id")
	}
	if user.ScopeId != scopeId {
		return autherror.PermissionDenied("wrong scope id")
	}
	if req.NewEmail == user.Email{
		return autherror.InvalidData("mail not change")
	}

	// and pre check
	listReq := &params.UserListRequest{}
	listReq.PartnerId = pId
	listReq.ScopeId = scopeId
	listReq.Mail = req.NewEmail
	listReq.Limit = 1
	duplicateNameUsers, _, _ := s.repoService.ListUser(listReq)
	if len(duplicateNameUsers) != 0 {
		return autherror.InvalidData("mail already exist")
	}
	listReq.Mail = ""
	listReq.Name = req.NewEmail
	duplicateCodeUsers, _, _ := s.repoService.ListUser(listReq)
	if len(duplicateCodeUsers) != 0 {
		return  autherror.InvalidData("name already exist")
	}
	listReq.Name = ""
	listReq.Mail = ""
	listReq.Code = req.NewEmail
	duplicateMailUsers, _, _ := s.repoService.ListUser(listReq)
	if len(duplicateMailUsers) != 0 {
		return  autherror.InvalidData("code already exist")
	}

	user.Email = req.NewEmail
	user.Code = req.NewEmail
	user.Name = req.NewEmail
	pwd := RandomPwd()
	user.ResetPwd(pwd)
	user.UpdatedBy = uId
	if err := s.repoService.UpdateUser(user); err != nil {
		return autherror.DBError(err.Error())
	}
	if err := SendPwdInitEmail(user.Email, user.Email, pwd); err != nil {
		return autherror.DBError(err.Error())
	}
	return nil
}

func (s *VendorAccountServiceImpl) UpdateVendorManagerUser(ctx context.Context, req *params.ResetVendorManagerUserUpdateRequest) autherror.HexError {
	pId := ctx.Value("partner_id").(uint64)
	uId := ctx.Value("user_id").(uint64)
	scopeId := ctx.Value("scope_id").(uint64)
	if err := req.Validate(); err != nil {
		return autherror.InvalidData(err.Error())
	}
	user := s.repoService.GetUserByIdFromDB(req.UserId)
	if user.Type != enum.UserType_VendorManager {
		return autherror.InvalidData("user not vendor type")
	}
	if valid := user.ValidatePwd(req.OldPwd); !valid {
		return autherror.InvalidData("wrong old pwd")
	}
	if user.PartnerId != pId {
		return autherror.PermissionDenied("wrong partner id")
	}
	if user.ScopeId != scopeId {
		return autherror.PermissionDenied("wrong scope id")
	}
	user.ResetPwd(req.NewPwd)
	user.UpdatedBy = uId
	if req.Name != nil {
		user.Name = *req.Name
	}
	if err := s.repoService.UpdateUser(user); err != nil {
		return autherror.InvalidData(err.Error())
	}

	return nil
}
